﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml.Linq;

using LiteBlog.Common;
using LiteBlog.Common.Components;
using LiteBlog.Common.Entities;
using LiteBlog.Repositories.Contracts;
using LiteBlog.Repositories.Helpers;
using LiteBlog.Common.Enums;
using LiteBlog.Repositories.Framework;
using LiteBlog.Repositories.Common;

namespace LiteBlog.Repositories
{
    public class CommentRepository : DataAccess, ICommentRepository
    {
        private static string _dataPath;

        public CommentRepository(IDataContext context, IXmlHelper xmlHelper) : base(context, xmlHelper)
        {
            _path = context.GetPath(DataStoreType.Comment, null);
            _dataPath = context.DataPath;
        }

        public Comment Get(string id)
        {
            var root = XElement.Load(_path);
            var commentElem = root.Elements("Comment").SingleOrDefault(elem => (string)elem.Attribute("ID") == id);
            return commentElem != null ? GetFromElement(commentElem) : null;
        }

        public IEnumerable<Comment> Get()
        {
            var comments = new List<Comment>();
            var root = XElement.Load(_path);

            foreach (XElement commentElem in root.Elements("Comment"))
            {
                var comment = GetFromElement(commentElem);
                comments.Add(comment);
            }

            return comments;
        }

        public bool Create(Comment comment)
        {
            comment.ID = CreateInternal(comment);
            if (comment.IsApproved)
            {
                var postData = new PostCommentRepository(_context, _xmlHelper);
                postData.Create(comment);
            }
            return true;
        }

        public bool Update(string commentId, Comment comment)
        {
            // commentId won't change
            UpdateInternal(comment);
            if (comment.IsApproved)
            {
                var postData = new PostCommentRepository(_context, _xmlHelper);
                postData.Update(comment);
            }
            return true;
        }

        public bool Delete(string commentId)
        {
            var comment = Get(commentId);
            if (comment != null)
            {
                DeleteInternal(commentId);
                if (comment.IsApproved)
                {
                    var postData = new PostCommentRepository(_context, _xmlHelper);
                    postData.Delete(comment.ID, comment.FileID);
                }
            }
            return true;
        }

        public void Approve(string id)
        {
            var root = XElement.Load(_path);
            var commentElem = root.Elements("Comment").Single(elem => (string)elem.Attribute("ID") == id);
            if (!(bool)commentElem.Attribute("Approved"))
            {
                commentElem.SetAttributeValue("Approved", true);
                _xmlHelper.Save(root, _path);

                var comment = GetFromElement(commentElem);
                var postData = new PostCommentRepository(_context, _xmlHelper);
                postData.Create(comment);
            }
        }

        public void Reject(string id)
        {
            DeleteInternal(id);
        }

        internal string CreateInternal(Comment comment)
        {
            var root = _xmlHelper.Load(_path);
            int id = int.Parse(root.Attribute("TotalComments").Value);
            root.Attribute("TotalComments").Value = (++id).ToString();

            XElement commentElem = new XElement(
                "Comment",
                new XAttribute("ID", id),
                new XAttribute("FileID", comment.FileID),
                new XAttribute("Name", comment.Name),
                new XAttribute("Url", comment.Url ?? string.Empty),
                new XAttribute("Ip", comment.Ip),
                new XAttribute("Time", comment.Time.ToString(Constants.DateTimeFormat, CultureInfo.InvariantCulture)),
                new XAttribute("IsAuthor", comment.IsAuthor),
                new XAttribute("Approved", comment.IsApproved));
            commentElem.Value = comment.Text;
            root.AddFirst(commentElem);

            _xmlHelper.Save(root, _path);
            return id.ToString();
        }

       
        private Comment GetFromElement(XElement commentElem)
        {
            var comment = new Comment();
            comment.ID = commentElem.Attribute("ID").Value;
            comment.FileID = commentElem.Attribute("FileID").Value;
            comment.Name = commentElem.Attribute("Name").Value;
            comment.Ip = commentElem.Attribute("Ip").Value;
            comment.Url = commentElem.Attribute("Url").Value;
            comment.Text = HttpUtility.HtmlDecode(_xmlHelper.GetInnerXml(commentElem));
            comment.IsApproved = bool.Parse(commentElem.Attribute("Approved").Value);
            if (commentElem.Attribute("IsAuthor") != null)
            {
                comment.IsAuthor = bool.Parse(commentElem.Attribute("IsAuthor").Value);
            }
            comment.FormattedTime = commentElem.Attribute("Time").Value;
            comment.Time = DateTime.ParseExact(
                commentElem.Attribute("Time").Value,
                Constants.DateTimeFormat,
                CultureInfo.InvariantCulture);
            return comment;
        }

        private void UpdateInternal(Comment comment)
        {
            var root = XElement.Load(_path);
            var commentElem = root.Elements("Comment").Single(elem => (string)elem.Attribute("ID") == comment.ID);
            commentElem.SetAttributeValue("Name", comment.Name);
            commentElem.SetAttributeValue("Url", comment.Url ?? string.Empty);
            commentElem.SetAttributeValue("IsAuthor", comment.IsAuthor);
            commentElem.Value = comment.Text;
            _xmlHelper.Save(root, _path);
        }

        private void DeleteInternal(string id)
        {
            var root = XElement.Load(_path);
            var commentElem = root.Elements("Comment")
                                  .Single(elem => (string)elem.Attribute("ID") == id);
            commentElem.Remove();
            _xmlHelper.Save(root, _path);
        }
    }
}